#include <Windows.h>
#include <tchar.h>
#include <algorithm>
#include "debug.h"
#include "Gomoku.h"
#include "Board.h"
#include "PatternManager.h"
#include "PatternFileManager.h"

PatternManager& PatternManager::m_instance = PatternManager();

pattern::pattern()
{
}

pattern::~pattern()
{
	if (Form != NULL)
	{
		for (int k = 0; k < TotalPositions; k++)
		{
			for (int i = 0; i < Size; i++)
			{
				delete [] Form[k][i];
			}
			delete [] Form[k];
		}
		delete [] Form;
	}
}

static pattern* LoadPattern(std::string _filePath)
{
	std::list<std::string> content;
	PatternFileManager pfm;
	pattern* p = NULL;

	pfm.LoadFile(_filePath, content);
	std::list<std::string>::iterator itb = content.begin(), ite = content.end();
	PatternManager::Instance().InitPattern(p, pfm.GetTotalPosition(), pfm.GetSize());
	for (int k = 0; k < p->TotalPositions; k++)
	{
		for (int i = 0; i < p->Size && itb != ite; i++, ++itb)
		{
			for (int j = 0; j < p->Size && itb != ite; j++)
			{
				if ((*itb).length() == 0) ++itb;
				p->Form[k][i][j] = (*itb).at(j) - '0';
				//std::cout << (char)(p->Form[k][i][j] + '0') << " ";
			}
			//std::cout << std::endl;
		}
		//std::cout << std::endl;
	}
	COUT_ENDL(std::string(_filePath + " loaded"));
	return (p);
}

PatternManager::PatternManager()
{
	WIN32_FIND_DATA fileInfo;

	HANDLE hFile = ::FindFirstFile(_T("patterns\\*.txt"), &fileInfo);
	if(hFile != INVALID_HANDLE_VALUE)
	{
		do
		{
			if(fileInfo.cFileName[0] != '.')
			{
				char str[4096];
				wcstombs_s(NULL, str, fileInfo.cFileName, wcslen(fileInfo.cFileName) + 1);

				std::string filePath;
				filePath = std::string("patterns\\") + std::string(str);
				pattern* p = LoadPattern(filePath);
				this->m_patterns.push_back(p);
			}
		} while(::FindNextFile(hFile, &fileInfo) == TRUE);
	}
}

PatternManager::~PatternManager()
{
	std::list<pattern*>::iterator it = m_patterns.begin();
	for (; it != m_patterns.end(); ++it)
	{
		delete *it;
	}
	m_patterns.clear();
}

PatternManager& PatternManager::Instance()
{
	return (PatternManager::m_instance);
}

void PatternManager::InitPattern(pattern*& _p, int _totalPositions, int _size)
{
	
	_p = new pattern();
	_p->TotalPositions = _totalPositions;
	_p->Size = _size;
	_p->Form = new char**[_totalPositions];
	for (int i = 0; i < _totalPositions; i++)
	{
		_p->Form[i] = new char*[_size];
		for (int j = 0; j < _size; j++)
			_p->Form[i][j] = new char[_size];
	}
}

static void PrintForm(pattern& _p, int _position)
{
#ifdef _DEBUG
	for (int i = 0; i < _p.Size; i++)
	{
		for (int j = 0; j < _p.Size; j++)
		{
			std::cout << (char)(_p.Form[_position][i][j] + '0') << " ";
		}
		std::cout << std::endl;
	}
#endif
}


static BoardCase** CreateCheckArea(pattern* _p, int _x, int _y, Board& _board)
{
	int size = (_p->Size * 2) - 1;
	if ((_x - size / 2) < 0 || (_y - size / 2) < 0)
		return (NULL);

	if ((_x + size / 2) >= _board.Width || (_y + size / 2) >= _board.Height)
		return (NULL);

	BoardCase** (area) = new BoardCase*[size];
	for (int i = 0; i < size; i++)
		(area)[i] = new BoardCase[size];
	BoardCase** cases = _board.Cases;
	int off_y = size / 2 * -1;
	for (int y = 0; y < size; y++, off_y++)
	{
		int off_x = (size / 2) * -1;
		for (int x = 0; x < size; x++, off_x++)
		{
			area[y][x] = cases[(_y + off_y)][(_x + off_x)];
		}
	}
	return (area);
}

static bool checkIfRuleThreeIsCompromised(BoardCase& _case, int _y, int _x, pattern& _p, int _position, int _patternX, int _patternY)
{
	int nbDirections = 8;

	for (int i = 0; i < nbDirections; i++)
	{
		BoardCase ennemyDirection = Board::ConvertDirectionToEnnemy(i + 1);
		if (GET_ALIGN_ENMY(_case) & ennemyDirection)
		{
			int oppositeDirection = Board::GetOppositeDirection(i + 1);
			BoardCase allyDirection = Board::ConvertDirectionToAlly(oppositeDirection);
			if (GET_ALIGN_ALLY(_case) & allyDirection)
			{
				int offx = Board::GetXIncrementFromDirection(oppositeDirection);
				int offy = Board::GetYIncrementFromDirection(oppositeDirection);
				if (_patternX + offx >= 0 && _patternX + offx < _p.Size && _patternY + offy >= 0 && _patternY + offy < _p.Size)
				{
					if (_p.Form[_position][_patternY + offy][_patternX + offx] == 1 || _p.Form[_position][_patternY + offy][_patternX + offx] == 3)
					{
						offx += offx;
						offy += offy;
						if (_patternX + offx >= 0 && _patternX + offx < _p.Size && _patternY + offy >= 0 && _patternY + offy < _p.Size)
							if (_p.Form[_position][_patternY + offy][_patternX + offx] == 1 || _p.Form[_position][_patternY + offy][_patternX + offx] == 3)
								return (true);
					}
				}
			}
		}
	}
	return (false);
}

bool PatternManager::CheckPattern(pattern* _p, int _x, int _y, BoardCase _color, Board& _board, bool _verbose)
{
	BoardCase** area = CreateCheckArea(_p, _x, _y, _board);

	if (area == NULL)
	{
		//std::cout << "No area found" << std::endl;
		return (false);
	}

	for (int k = 0; k < _p->TotalPositions; k++)
	{
		for (int y = 0; y < _p->Size; y++)
		{
			for (int x = 0; x < _p->Size; x++)
			{
				int matches = 0;
				for (int i = 0; i < _p->Size; i++)
				{
					if (_p->Form[k][i][0] == 4)
						continue ;
					for (int j = 0; j < _p->Size; j++)
					{
						if (_p->Form[k][i][j] == 5 || _p->Form[k][i][j] == 0)
							continue ;
						if ((_p->Form[k][i][j] == 1 || _p->Form[k][i][j] == 3) && ((i == (_p->Size * 2 - 1) / 2 - y && j == (_p->Size * 2 - 1) / 2 - x)
							|| GET_COLOR(area[y + i][x + j]) == _color))
						{
							if (_p->Form[k][i][j] == 3)
							{
								if (checkIfRuleThreeIsCompromised(area[y + i][x + j], y + i, x + j, *_p, k, j, i))
								{
									delete [] area;
									return (false);
								}
							}
							matches++;
						}
						if (matches == 5)
						{
							if (_verbose == true)
							{
								COUT_ENDL("===FORM FOUND===");
								PrintForm(*_p, k);
								COUT_ENDL("================");
							}
							delete [] area;
							return (true);
						}
					}
				}
			}
		}
	}
	delete [] area;
	return (false);
}

std::list<pattern*> const& PatternManager::GetPatterns() const
{
	return (m_patterns);
}
